package jxta.chat.search;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jxta.chat.entities.PeerNode;
import jxta.chat.events.JxtaChatEvent;
import jxta.chat.events.JxtaChatListener;
import jxta.test.JxtaManager;
import net.jxta.discovery.DiscoveryEvent;
import net.jxta.discovery.DiscoveryListener;
import net.jxta.discovery.DiscoveryService;
import net.jxta.document.Advertisement;
import net.jxta.endpoint.EndpointAddress;
import net.jxta.endpoint.EndpointService;
import net.jxta.endpoint.Messenger;
import net.jxta.peergroup.PeerGroup;
import net.jxta.protocol.DiscoveryResponseMsg;
import net.jxta.protocol.PeerAdvertisement;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.protocol.RouteAdvertisement;

public class PeerSearcher implements DiscoveryListener{
	
	private Map<String, PeerNode> foundPeers = new HashMap<String, PeerNode>();
	public final static int SEARCH_TRESHOLD = 50;
	
	private DiscoveryService discovery = null;
	private EndpointService endPoint = null;
	private String peerName;
	
	private Thread searchThread = null;
	
	private List<JxtaChatListener> foundedPeerListeners = new ArrayList<JxtaChatListener>();
	
	public PeerSearcher(PeerGroup group, String currPeerName){
		discovery = group.getDiscoveryService();
		endPoint = group.getEndpointService();
		peerName = currPeerName;
		
		//this.setDaemon(true);
	}
		
	public void addChatEventListener(JxtaChatListener listener){
		foundedPeerListeners.add(listener);
	}
	
	private void notifyPeerFound(JxtaChatEvent event){
		for (JxtaChatListener listener : foundedPeerListeners){
			listener.jxtaChatEvent(event);
		}
	}		

	// Koby- Replaced the method for check
	
	public void discoveryEvent(DiscoveryEvent de) {		
		DiscoveryResponseMsg rm = de.getResponse();
	    if (rm.getDiscoveryType() == DiscoveryService.ADV) {
	    	process(rm.getAdvertisements());
	    }
	}
	 
	/*@Override
	public void discoveryEvent(DiscoveryEvent arg0) {
		Enumeration<Advertisement> advs = arg0.getResponse().getAdvertisements();
		
		//System.out.println("Got " + arg0.getResponse().getResponseCount() + " responses from peers: ");
		
		while (advs.hasMoreElements()){
			Advertisement currObject = advs.nextElement();
			
			if (!(currObject instanceof PeerAdvertisement)){
				System.out.println("Wrong adv type. got " + currObject.getClass().toString());
				continue;					
			}
			
			PeerAdvertisement pAdv = (PeerAdvertisement)currObject;
			
			// If it's the local peer or it isn't the current group peer we'll continue			
			if (pAdv.getName().equalsIgnoreCase(peerName) || (!pAdv.getName().startsWith(JxtaManager.CHAT_PREFIX))){
				continue;
			}
			
			if (!foundPeers.containsKey(pAdv.getName())){
				
				System.out.println("Found peer " + pAdv.getName() + " check if he is alive");
				
				// TODO Change to constant
				try
				{					
					process(discovery.getLocalAdvertisements(DiscoveryService.PEER, null, null));
				}
				catch(Exception e){
					System.out.println("Error calling to process. error: " + e.getMessage());
				}
				
				//discovery.getLocalAdvertisements(arg0, arg1, arg2)
			}
		}
		
	}*/
	
	private void process(Enumeration advertisements){		
		PipeAdvertisement pa;
        Object o;
        String name;
		
		while (advertisements != null &&
	            advertisements.hasMoreElements()) {
	        o = advertisements.nextElement();
	
	        if (o instanceof PipeAdvertisement) {
	            pa = (PipeAdvertisement) o;
	            name = pa.getName();
	
	            if (name != null){
	            		System.out.println("found peer with name " + name);
	            		this.notifyPeerFound(new JxtaChatEvent(pa, "", pa.getName().substring(0, pa.getName().indexOf(".")), 
	            							 pa.getName().substring(pa.getName().indexOf(".") + 1)));
	            }
	        }
	    }
	}
	
	// TODO Koby-Removed the method for check
	/*private void process(Enumeration<Advertisement> advs){
		while (advs.hasMoreElements()){
			Advertisement currObject = advs.nextElement();
			
			if (!(currObject instanceof RouteAdvertisement)){					
				continue;					
			}			
			else{
				RouteAdvertisement adv = (RouteAdvertisement)currObject;
				
				List<EndpointAddress> peerAddresses = adv.getDestEndpointAddresses();
				
				// Find the available peer address
				for (EndpointAddress currAddress : peerAddresses){
					
					Messenger msg = endPoint.getMessenger(currAddress);						
					
					if (msg != null){
						try{
						
							String name = "";
							
							// Search for the peer name
							Enumeration<Advertisement> peerAdvs = discovery.getLocalAdvertisements(DiscoveryService.PEER, PipeAdvertisement.IdTag, adv.getDestPeerID().toString());
							
							while (peerAdvs.hasMoreElements()){
								Advertisement current = peerAdvs.nextElement();
								
								// If it's a PeerAdvertisment we'll get the name and finish
								if (current instanceof PeerAdvertisement){
									name = ((PeerAdvertisement)current).getName();
									break;
								}
							}
							
							PeerNode p = new PeerNode(adv.getDestPeerID(), name, msg);
							
							System.out.println("Name: " + name + " ID: " + p.getPeerId() + ". peer alive");
							
							synchronized (foundPeers){
								foundPeers.put(name, p);
							}
							
							// Notify about the new peer
							JxtaChatEvent event = new JxtaChatEvent(p, "", 
									name.substring(0, JxtaManager.CHAT_PREFIX.length() - 2),
									name.substring(JxtaManager.CHAT_PREFIX.length(), name.length()));
						
							notifyPeerFound(event);
															
						}
						catch (IOException e){
							
							// TODO write to log
							
							// Keep searching
							continue;
						}
						break;
					}					
				}
			}		
		}
	}*/
	
	/**
	 * An inner class that listen for RouteAdvertisement in order to get the founded
	 * peer address and check it's connection status 
	 * 
	 * @author KobyKarin
	 *
	 */
	private class EndpointAddressListener implements DiscoveryListener{

		// FIXME marked due to ant errors
		
		//@Override
		public void discoveryEvent(DiscoveryEvent arg0) {
					
			Enumeration<Advertisement> advs = arg0.getResponse().getAdvertisements();
			
			while (advs.hasMoreElements()){
				Advertisement currObject = advs.nextElement();
				
				if (!(currObject instanceof RouteAdvertisement)){					
					continue;					
				}			
				else{
					RouteAdvertisement adv = (RouteAdvertisement)currObject;
					
					List<EndpointAddress> peerAddresses = adv.getDestEndpointAddresses();
					
					// Find the available peer address
					for (EndpointAddress currAddress : peerAddresses){
						
						Messenger msg = endPoint.getMessenger(currAddress);						
						
						if (msg != null){
							try{
							
								String name = "";
								
								// Search for the peer name
								Enumeration<Advertisement> peerAdvs = discovery.getLocalAdvertisements(DiscoveryService.PEER, PipeAdvertisement.IdTag, adv.getDestPeerID().toString());
								
								while (peerAdvs.hasMoreElements()){
									Advertisement current = peerAdvs.nextElement();
									
									// If it's a PeerAdvertisment we'll get the name and finish
									if (current instanceof PeerAdvertisement){
										name = ((PeerAdvertisement)current).getName();
										break;
									}
								}
								
								PeerNode p = new PeerNode(adv.getDestPeerID(), name, msg);
								
								System.out.println("Name: " + name + " ID: " + p.getPeerId() + ". peer alive");
								
								synchronized (foundPeers){
									foundPeers.put(name, p);
								}
								
								// Notify about the new peer
								JxtaChatEvent event = new JxtaChatEvent(p, "", 
										name.substring(0, JxtaManager.CHAT_PREFIX.length() - 2),
										name.substring(JxtaManager.CHAT_PREFIX.length(), name.length()));
							
								notifyPeerFound(event);
																
							}
							catch (IOException e){
								
								// TODO write to log
								
								// Keep searching
								continue;
							}
							break;
						}					
					}
				}		
			}
		}				
				
	}
	
	public void search(){
		searchThread = new SearchThread(discovery);
		searchThread.start();
	}
	
	private class SearchThread extends Thread{
		
		private DiscoveryService discovery;
		
		public SearchThread(DiscoveryService service){
			setDaemon(true);
			discovery = service;
		}
		
		@Override
		public void run() {
			

			//while (true){			
				
				long waittime = 10 * 1 * 1000L;
				
				System.out.println("start searching for peers");				

		        //try {	          
		                discovery.getRemoteAdvertisements(
		                        null,
		                        //DiscoveryService.PEER,
		                        DiscoveryService.ADV,
		                        PipeAdvertisement.NameTag,
			                    JxtaManager.CHAT_PREFIX + "*", // one advertisement response is all we are looking for
		                        SEARCH_TRESHOLD, PeerSearcher.this);
		                
		                // Sleep before start another search
		                //sleep(waittime);
		            /*}
		        	catch(InterruptedException e){
		        		System.out.println("Error while sleeping in " + this.getClass().toString() + ". error: " + e.getMessage());
		        	}
		        	catch (Exception e) {
		        		e.printStackTrace();
		        	}	*/        	
		        //}
				//System.out.println("finish searching for peers");
					
		}
	}

	/**
	 * Inner class that represents a founded peer
	 * 
	 * @author KobyKarin
	 *
	 */
	/*private class FoundedPeer{
		
		private Messenger messenger = null;
		private String peerName = null;
		private PeerID peerId;
		
		public FoundedPeer(PeerID id, String name, Messenger msg){
			messenger = msg;
			peerId = id;
			peerName = name;
		}

		public Messenger getMessenger() {
			return messenger;
		}

		public void setMessenger(Messenger messenger) {
			this.messenger = messenger;
		}

		public String getPeerName() {
			return peerName;
		}

		public void setPeerName(String peerName) {
			this.peerName = peerName;
		}

		public PeerID getPeerId() {
			return peerId;
		}

		public void setPeerId(PeerID peerId) {
			this.peerId = peerId;
		}
		
	}*/
	
}
